package com.sparrow.source.socket.netty.hander;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sparrow.source.common.CommonConstant;
import com.sparrow.source.common.dto.LibMatInfo;
import com.sparrow.source.common.dto.UnvVerifyReport;
import com.sparrow.source.common.dto.convert.UnvVerifyReportConvert;
import com.sparrow.source.common.utils.JsonFormatUtils;
import com.sparrow.source.common.utils.RedisUtils;
import com.sparrow.source.persistense.IasVerifyReport;
import com.sparrow.source.service.IIasVerifyReportService;
import com.sparrow.source.socket.bean.AccessBackDTO;
import com.sparrow.source.socket.bean.HeartBackDTO;
import com.sparrow.source.socket.bean.HeartRequestDTO;
import com.sparrow.source.socket.constant.LapiUriConstat;
import com.sparrow.source.socket.manager.DeviceChannelContext;
import com.sparrow.source.socket.manager.ResponseDeviceManager;
import com.sparrow.source.socket.netty.factory.ChannelFactory;
import com.sparrow.source.socket.netty.factory.HttpResponseFactory;
import com.sparrow.source.third.UnvClient;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.CharsetUtil;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Objects;

/**
 * Copyright (C),  2018-2025, ZheJiang Uniview Technologies Co., Ltd. All rights reserved.
 * <http://www.uniview.com/>
 * <p>
 * FileName : HttpHeartHandler
 *
 * @Author : s04180
 * Date     : 2019/12/30 17:04
 * DESCRIPTION:
 * <p>
 * History:
 * DATE        NAME        DESC
 */
@Component
public class LapiServerHandler extends SimpleChannelInboundHandler {

    /**
     * 日志信息
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(LapiServerHandler.class);

    /**
     * 设备序列号
     */
    public volatile String serialNo;

    private static StringBuffer stickyResp = new StringBuffer();

    private IIasVerifyReportService verifyReportService;

    private UnvVerifyReportConvert unvVerifyReportConvert;

    private UnvClient unvClient;

    private RedisUtils redisUtils;

    public LapiServerHandler(IIasVerifyReportService reportService, UnvVerifyReportConvert reportConvert, UnvClient unvClient, RedisUtils redisUtils) {
        this.verifyReportService = reportService;
        this.unvVerifyReportConvert = reportConvert;
        this.unvClient = unvClient;
        this.redisUtils = redisUtils;
    }

    /**
     * 读取通道,过滤并处理心跳消息
     *
     * @param ctx 通道上下文
     * @param msg 消息
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            //区分是response 还是 request
            if (msg instanceof FullHttpRequest) {
                FullHttpRequest httpRequest = (FullHttpRequest) msg;
                // 请求uri
                String requestUri = httpRequest.uri();
                // 消息体内容
                String content = httpRequest.content().toString(CharsetUtil.UTF_8);
                // 心跳保活消息
                if (requestUri.contains(LapiUriConstat.HEART_URI)) {
                    //回包并存下设备的连接通道ctx
                    saveDeviceCtx(content, ctx);
                    heartResponse(ctx);
                } else if (httpRequest.uri().contains(LapiUriConstat.ACCESS_RECEIVE_URI) || stickyResp.toString().length() > 0) {
                    // 设备上报的出入记录信息
                    String accessRecord = httpRequest.content().toString(CharsetUtil.UTF_8);
                    JSONObject jsonObject = JSON.parseObject(accessRecord);
                    Long seq = jsonObject.getLong("Seq");
                    Long uploadTime = jsonObject.getLong("Timestamp") * 1000;
                    int notificationType = jsonObject.getInteger("NotificationType");
                    long passTime = jsonObject.getJSONArray("FaceInfoList").getJSONObject(0).getLong("Timestamp") * 1000;
                    accessResponse(ctx, seq, 0);
                    LOGGER.debug("Access Record Seq:{},UploadTime:{},,PassTime:{},NotificationType:{}", seq, DateFormatUtils.format(uploadTime, "yyyy-MM-dd HH:mm:ss"),
                            DateFormatUtils.format(passTime, "yyyy-MM-dd HH:mm:ss"), notificationType);
                    // 处理上报
                    peopleVerificationReport(accessRecord);
                } else {
                    // 其他请求信息暂未处理,直接透传到后面可自定义的 handle
                    ctx.fireChannelRead(msg);
                }
                // 平台请求,设备的回复消息
            } else if (msg instanceof FullHttpResponse) {
                FullHttpResponse response = (FullHttpResponse) msg;
                HttpResponseFactory.responseHandle(ctx, response);
            } else {
                ctx.fireChannelRead(msg);
            }
        } catch (Exception e) {
            LOGGER.error("Read [{}] channel msg[{}] failed for:{}", ctx.channel().remoteAddress().toString()
                    , JSON.toJSONString(msg), e.getMessage(), e);
        }
    }

    /**
     * 心跳回包拼装
     *
     * @return
     * @throws Exception
     */
    private void heartResponse(ChannelHandlerContext ctx) {
        HeartBackDTO heartBackDO = new HeartBackDTO(LapiUriConstat.HEART_URI, 0, new Date());
        ResponseDeviceManager.responseDevice(ctx, heartBackDO);
        LOGGER.info("[SERIAL_NO][{}] 心跳回包正常！客户端IP: {}", serialNo, ctx.channel().remoteAddress());
        verifyReportService.updateOnlineStatus(serialNo, true);
    }

    /**
     * 响应终端
     * StatusCode：0：平台接收成功，1：平台接收失败
     * StatusString：Succeed/Common Fail
     * RecordID：通行记录ID（对应过人记录里的"Seq"字段）
     *
     * @param ctx
     * @param seq 记录id
     * @param statusCode 0-成功 1-失败
     */
    public void accessResponse(ChannelHandlerContext ctx, Long seq, Integer statusCode) {
        AccessBackDTO accessRecordBack = new AccessBackDTO(LapiUriConstat.ACCESS_RESPONSE_URI, statusCode, seq);
        ResponseDeviceManager.responseDevice(ctx, accessRecordBack);
    }

    /**
     * 保存链接
     *
     * @param strRequest
     * @param ctx
     * @throws Exception
     */
    private void saveDeviceCtx(String strRequest, ChannelHandlerContext ctx) {
        HeartRequestDTO request = JSONObject.parseObject(strRequest, HeartRequestDTO.class);
        String serialNo = request.getStrDeviceCode();
        this.serialNo = serialNo;
        DeviceChannelContext deviceCtx = ChannelFactory.getChannelBySerialNo(serialNo);
        if (Objects.isNull(deviceCtx)) {
            //添加设备的链接通道
            ChannelFactory.addChannel(serialNo, ctx);
        } else {
            // 刷新设备对应的链接通道
            ChannelFactory.freshChannel(serialNo, ctx);
        }
    }

    private void peopleVerificationReport(String message) {
        try {
            if (!message.contains("Remark")) {
                //报文粘包 未结束
                if (message.contains(CommonConstant.HTTP_CONSTANT)) {
                    stickyResp.append(message.substring(message.indexOf(CommonConstant.JSON_PREFIX)));
                } else {
                    stickyResp.append(message);
                }
                return;
            } else {
                stickyResp.append(message);
            }
            if (stickyResp.toString().contains(CommonConstant.HTTP_CONSTANT)) {
                stickyResp = new StringBuffer(stickyResp.substring(message.indexOf(CommonConstant.JSON_PREFIX)));
            }

            LOGGER.info("<<<<<<<<<<< PERSON_VERIFICATION <<<<<<<<<<<:\n{}", stickyResp);
            Object obj = JsonFormatUtils.parseJsonArray(stickyResp.toString()).get(0);
            UnvVerifyReport verifyReport = JSONObject.parseObject(String.valueOf(obj), UnvVerifyReport.class);
            //生成上报记录
            IasVerifyReport report = this.unvVerifyReportConvert.convertIasVerifyReport(verifyReport, stickyResp.toString());
            //            if (report == null) {
            //                ctx.channel().writeAndFlush(resp.toString());
            //            }
            stickyResp.setLength(0);
            //回复开门
            LibMatInfo libMatInfo = verifyReport.getLibMatInfoList().get(0);
            if (verifyReportService.haveCheckedInCurdate(verifyReport.getDeviceCode(), 1, libMatInfo.getMatchPersonId())) {
                unvClient.remoteControlGUI(verifyReport.getDeviceCode(), libMatInfo.getMatchStatus() == 1 ? 1 : 0, libMatInfo.getMatchPersonId(), 1);
            } else {
                // 需要先上班打卡，再进行通道打卡
                unvClient.remoteControlGUI(verifyReport.getDeviceCode(), 0, libMatInfo.getMatchPersonId(), 0);
            }
            verifyReportService.saveReport(report);
        } catch (Exception e) {
            LOGGER.error("PERSON VERIFICATION handler error", e.getMessage(), e);
            //                unvClient.remoteControlGUI(verifyReport.getDeviceCode(), 0, libMatInfo.getMatchPersonId());
        }
    }

    /**
     * 添加channel时
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        Channel incoming = ctx.channel();
        LOGGER.debug("SimpleChatClient:" + incoming.remoteAddress() + "通道被添加");
    }

    /**
     * 删除channel时
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        LOGGER.debug("SimpleChatClient:{},通道被删除", channel.remoteAddress());
    }

    /**
     * 服务端监听到客户端不活动
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        //服务端接收到客户端掉线通知
        Channel incoming = ctx.channel();
        LOGGER.error("SimpleChatClient: {}  掉线", incoming.remoteAddress());
    }

    /**
     * 服务端监听到客户端活动
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        //服务端接收到客户端上线通知
        Channel incoming = ctx.channel();
        LOGGER.debug("SimpleChatClient:" + incoming.remoteAddress() + "上线");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        Channel channel = ctx.channel();
        LOGGER.error("SimpleChatClient:{},异常断开,{}", channel.remoteAddress(), cause);
        //出现异常判断下通道是否还可用
        if (!channel.isWritable()) {
            ctx.close();
            HttpResponseFactory.removeResponse(ctx);
        }
    }

}
